#pragma once

#include "funpl.h"
#include "ast.h"
#include "ast_meta.h"
#include <vector>

namespace funpl
{

namespace ast
{

/// Type node.
struct type : public node
{
	bool const_;

protected:
	/// Constructor.
	type(node_type::t type, node *parent = nullptr)
			: node(type, parent),
			const_(false) { }
};

struct expression;

/// Type node.
struct metaarguments : public node
{
	typedef std::vector<expression*> argument_vector;
	argument_vector arguments;

	/// Constructor.
	metaarguments(node *parent = nullptr)
			: node(node_type::metaarguments, parent) { }
};

/// Type node.
struct type_identifier : public type
{
	util::const_char_range name;

	metaarguments *arguments;

	type *nested;

	/// Constructor.
	type_identifier(node *parent = nullptr,
		const util::const_char_range &name = util::empty_const_char_range())
			: type(node_type::type_identifier, parent),
			name(name),
			arguments(),
			nested() { }
};

/// Inline metatype node.
struct inline_metatype : public type
{
	metatype *metatype;

	/// Constructor.
	inline_metatype(node *parent = nullptr,
		struct metatype *metatype = nullptr)
			: type(node_type::inline_metatype, parent),
			metatype( reparent(metatype, this) ) { }
};

/// Type node.
struct pointer_type : public type
{
	type *operand;

	/// Constructor.
	pointer_type(node *parent = nullptr,
		type *operand = nullptr)
			: type(node_type::pointer_type, parent),
			operand( reparent(operand, this) ) { }
};

struct expression;

/// Type node.
struct array_type : public type
{
	type *operand;

	expression *count;

	/// Constructor.
	array_type(node *parent = nullptr,
		expression *count = nullptr)
			: type(node_type::array_type, parent),
			operand(),
			count( reparent(count, this) ) { }
};

/// Type node.
struct function_type : public type
{
	typedef std::vector<type*> parameter_vector;
	parameter_vector parameters;

	type *returnType;
	bool returnRef;

	/// Constructor.
	function_type(node *parent = nullptr)
		: type(node_type::function_type, parent),
		returnType(),
		returnRef(false) { }
};

} // namespace

} // namespace